// This is an open source non-commercial project. Dear PVS-Studio, please check it.
// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com

// ReSharper disable CheckNamespace
// ReSharper disable CommentTypo
// ReSharper disable IdentifierTypo
// ReSharper disable InconsistentNaming
// ReSharper disable NonReadonlyMemberInGetHashCode

/* Util.cs --
 * Ars Magna project, http://arsmagna.ru
 */

#region Using directives

using System;
using System.Collections;
using System.Collections.Generic;

#endregion

#nullable enable

namespace AM.Collections;

/// <summary>
/// A holder class for various internal utility functions that need to be shared.
/// </summary>
internal static class Util
{
    /// <summary>
    /// Determine if a type is cloneable: either a value type or implementing
    /// ICloneable.
    /// </summary>
    /// <param name="type">Type to check.</param>
    /// <param name="isValue">Returns if the type is a value type, and does not implement ICloneable.</param>
    /// <returns>True if the type is cloneable.</returns>
    public static bool IsCloneableType (Type type, out bool isValue)
    {
        isValue = false;

        if (typeof (ICloneable).IsAssignableFrom (type))
        {
            return true;
        }

        if (type.IsValueType)
        {
            isValue = true;
            return true;
        }

        return false;
    }

    /// <summary>
    /// Returns the simple name of the class, for use in exception messages.
    /// </summary>
    /// <returns>The simple name of this class.</returns>
    public static string SimpleClassName (Type type)
    {
        var name = type.Name;

        // Just use the simple name.
        var index = name.IndexOfAny (new[] { '<', '{', '`' });
        if (index >= 0)
        {
            name = name.Substring (0, index);
        }

        return name;
    }

    /// <summary>
    /// Wrap an enumerable so that clients can't get to the underlying
    /// implementation via a down-cast.
    /// </summary>
    [Serializable]
    class WrapEnumerable<T> : IEnumerable<T>
    {
        IEnumerable<T> wrapped;

        /// <summary>
        /// Create the wrapper around an enumerable.
        /// </summary>
        /// <param name="wrapped">IEnumerable to wrap.</param>
        public WrapEnumerable (IEnumerable<T> wrapped)
        {
            this.wrapped = wrapped;
        }

        public IEnumerator<T> GetEnumerator()
        {
            return wrapped.GetEnumerator();
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return ((IEnumerable)wrapped).GetEnumerator();
        }
    }

    /// <summary>
    /// Wrap an enumerable so that clients can't get to the underlying
    /// implementation via a down-case
    /// </summary>
    /// <param name="wrapped">Enumerable to wrap.</param>
    /// <returns>A wrapper around the enumerable.</returns>
    public static IEnumerable<T> CreateEnumerableWrapper<T> (IEnumerable<T> wrapped)
    {
        return new WrapEnumerable<T> (wrapped);
    }

    /// <summary>
    /// Gets the hash code for an object using a comparer. Correctly handles
    /// null.
    /// </summary>
    /// <param name="item">Item to get hash code for. Can be null.</param>
    /// <param name="equalityComparer">The comparer to use.</param>
    /// <returns>The hash code for the item.</returns>
    public static int GetHashCode<T> (T item, IEqualityComparer<T> equalityComparer)
    {
        if (item == null)
        {
            return 0x1786E23C;
        }
        else
        {
            return equalityComparer.GetHashCode (item);
        }
    }
}
